/*-------------------------------------------------------------------------
 *
 * amapi.h
 *	  PostgreSQL 索引访问方法的 API。
 *
 * Copyright (c) 2015-2022, PostgreSQL Global Development Group
 *
 * src/include/access/amapi.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef AMAPI_H
#define AMAPI_H

#include "access/genam.h"

/*
 * 我们不希望在这里包含规划器头文件，因为索引AM的大部分实现
 * 并不涉及这些数据结构。为了允许在这里声明amcostestimate_function，
 * 使用前向结构引用。
 */
struct PlannerInfo;
struct IndexPath;

/* 同样，这个文件不应依赖于execnodes.h。 */
struct IndexInfo;


/*
 * amproperty API的属性。该列表涵盖了核心代码已知的属性，但
 * 索引AM可以通过匹配字符串属性名称来定义自己的属性。
 */
typedef enum IndexAMProperty
{
	AMPROP_UNKNOWN = 0,			/* 任何核心代码未知的内容 */
	AMPROP_ASC,					/* 列属性 */
	AMPROP_DESC,
	AMPROP_NULLS_FIRST,
	AMPROP_NULLS_LAST,
	AMPROP_ORDERABLE,
	AMPROP_DISTANCE_ORDERABLE,
	AMPROP_RETURNABLE,
	AMPROP_SEARCH_ARRAY,
	AMPROP_SEARCH_NULLS,
	AMPROP_CLUSTERABLE,			/* 索引属性 */
	AMPROP_INDEX_SCAN,
	AMPROP_BITMAP_SCAN,
	AMPROP_BACKWARD_SCAN,
	AMPROP_CAN_ORDER,			/* AM属性 */
	AMPROP_CAN_UNIQUE,
	AMPROP_CAN_MULTI_COL,
	AMPROP_CAN_EXCLUDE,
	AMPROP_CAN_INCLUDE
} IndexAMProperty;

/*
 * 我们使用这种结构类型的列表来跟踪操作符和支持函数，
 * 在构建或添加到opclass或opfamily时。amadjustmembers函数
 * 接收这些结构的列表，并被允许修改其"ref"字段。
 *
 * "ref"字段定义了pg_amop或pg_amproc条目应如何依赖于相关对象
 * （即，使用哪种依赖类型，以及它应依赖于哪个opclass或opfamily）。
 *
 * 如果ref_is_hard为true，则该条目将对操作符或支持函数具有
 * NORMAL依赖关系，并且对opclass或opfamily具有INTERNAL依赖关系。
 * 这将强制在删除操作符或支持函数时删除opclass或opfamily，
 * 并要求使用CASCADE选项。也不允许ALTER OPERATOR FAMILY DROP。
 * 这是对于对opclass至关重要的对象的正确行为。
 *
 * 如果ref_is_hard为false，则该条目将对操作符或支持函数具有
 * AUTO依赖关系，并且对opclass或opfamily也具有AUTO依赖关系。
 * 这允许ALTER OPERATOR FAMILY DROP，并且如果操作符或支持
 * 函数被删除，则会自动发生。这是针对不重要（"宽松"）对象的
 * 正确行为。
 *
 * 我们还对lefttype/righttype做出与对操作符或支持函数的
 * 依赖关系同等强度的依赖，除非这些依赖关系与对操作符或
 * 支持函数的依赖关系是冗余的。
 */
typedef struct OpFamilyMember
{
	bool		is_func;		/* 这是操作符还是支持函数？ */
	Oid			object;			/* 操作符或支持函数的OID */
	int			number;			/* 策略或支持函数编号 */
	Oid			lefttype;		/* lefttype */
	Oid			righttype;		/* righttype */
	Oid			sortfamily;		/* 排序操作符的排序opfamily，或0 */
	bool		ref_is_hard;	/* 硬依赖还是软依赖？ */
	bool		ref_is_family;	/* 依赖于opclass还是opfamily？ */
	Oid			refobjid;		/* opclass或opfamily的OID */
} OpFamilyMember;


/*
 * 回调函数签名 --- 有关更多信息，请参见indexam.sgml。
 */

/* 构建新索引 */
typedef IndexBuildResult *(*ambuild_function) (Relation heapRelation,
											   Relation indexRelation,
											   struct IndexInfo *indexInfo);

/* 构建空索引 */
typedef void (*ambuildempty_function) (Relation indexRelation);

/* 插入此元组 */
typedef bool (*aminsert_function) (Relation indexRelation,
								   Datum *values,
								   bool *isnull,
								   ItemPointer heap_tid,
								   Relation heapRelation,
								   IndexUniqueCheck checkUnique,
								   bool indexUnchanged,
								   struct IndexInfo *indexInfo);

/* 批量删除 */
typedef IndexBulkDeleteResult *(*ambulkdelete_function) (IndexVacuumInfo *info,
														 IndexBulkDeleteResult *stats,
														 IndexBulkDeleteCallback callback,
														 void *callback_state);

/* VACUUM后的清理 */
typedef IndexBulkDeleteResult *(*amvacuumcleanup_function) (IndexVacuumInfo *info,
															IndexBulkDeleteResult *stats);

/* indexscan是否可以返回IndexTuples？ */
typedef bool (*amcanreturn_function) (Relation indexRelation, int attno);

/* 估算索引扫描的成本 */
typedef void (*amcostestimate_function) (struct PlannerInfo *root,
										 struct IndexPath *path,
										 double loop_count,
										 Cost *indexStartupCost,
										 Cost *indexTotalCost,
										 Selectivity *indexSelectivity,
										 double *indexCorrelation,
										 double *indexPages);

/* 解析索引选项 */
typedef bytea *(*amoptions_function) (Datum reloptions,
									  bool validate);

/* 报告 AM、索引或索引列属性 */
typedef bool (*amproperty_function) (Oid index_oid, int attno,
									 IndexAMProperty prop, const char *propname,
									 bool *res, bool *isnull);

/* 在进度报告中使用的阶段名称 */
typedef char *(*ambuildphasename_function) (int64 phasenum);

/* 验证此 AM 的 opclass 定义 */
typedef bool (*amvalidate_function) (Oid opclassoid);

/* 验证要添加到 opclass/family 的操作符和支持函数 */
typedef void (*amadjustmembers_function) (Oid opfamilyoid,
										  Oid opclassoid,
										  List *operators,
										  List *functions);

/* 准备进行索引扫描 */
typedef IndexScanDesc (*ambeginscan_function) (Relation indexRelation,
											   int nkeys,
											   int norderbys);

/* (重新)开始索引扫描 */
typedef void (*amrescan_function) (IndexScanDesc scan,
								   ScanKey keys,
								   int nkeys,
								   ScanKey orderbys,
								   int norderbys);

/* 下一个有效元组 */
typedef bool (*amgettuple_function) (IndexScanDesc scan,
									 ScanDirection direction);

/* 获取所有有效元组 */
typedef int64 (*amgetbitmap_function) (IndexScanDesc scan,
									   TIDBitmap *tbm);

/* 结束索引扫描 */
typedef void (*amendscan_function) (IndexScanDesc scan);

/* 标记当前扫描位置 */
typedef void (*ammarkpos_function) (IndexScanDesc scan);

/* 恢复标记的扫描位置 */
typedef void (*amrestrpos_function) (IndexScanDesc scan);

/*
 * 回调函数签名 - 用于并行索引扫描。
 */

/* 估算并行扫描描述符的大小 */
typedef Size (*amestimateparallelscan_function) (void);

/* 准备进行并行索引扫描 */
typedef void (*aminitparallelscan_function) (void *target);

/* (重新)开始并行索引扫描 */
typedef void (*amparallelrescan_function) (IndexScanDesc scan);

/*
 * 索引 AM 的 API 结构。注意这必须存储在一块分配的
 * 内存中。
 */
typedef struct IndexAmRoutine
{
	NodeTag		type;

	/*
	 * 可以遍历/搜索此 AM 的策略总数（操作符）。
	 * 如果 AM 没有固定的策略分配，则为零。
	 */
	uint16		amstrategies;
	/* 此 AM 使用的支持函数的总数 */
	uint16		amsupport;
	/* opclass 选项支持函数编号或 0 */
	uint16		amoptsprocnum;
	/* AM是否支持根据索引列的值进行ORDER BY？ */
	bool		amcanorder;
	/* AM是否支持根据操作符在索引列上进行ORDER BY的结果？ */
	bool		amcanorderbyop;
	/* AM是否支持向后扫描？ */
	bool		amcanbackward;
	/* AM是否支持UNIQUE索引？ */
	bool		amcanunique;
	/* AM是否支持多列索引？ */
	bool		amcanmulticol;
	/* AM 是否要求扫描在第一个索引列上具有约束？ */
	bool		amoptionalkey;
	/* AM 是否处理 ScalarArrayOpExpr 资格？ */
	bool		amsearcharray;
	/* AM 是否处理 IS NULL/IS NOT NULL 资格？ */
	bool		amsearchnulls;
	/* 索引存储数据类型可以不同于列数据类型吗？ */
	bool		amstorage;
	/* 这种类型的索引可以聚集吗？ */
	bool		amclusterable;
	/* AM 是否处理谓词锁？ */
	bool		ampredlocks;
	/* AM 是否支持并行扫描？ */
	bool		amcanparallel;
	/* AM 是否支持包含 WITH 子句的列？ */
	bool		amcaninclude;
	/* AM是否使用maintenance_work_mem？ */
	bool		amusemaintenanceworkmem;
	/* 并行vacuum标志的OR。请参见vacuum.h中的标志。 */
	uint8		amparallelvacuumoptions;
	/* 索引中存储的数据类型，如果是变量则为InvalidOid */
	Oid			amkeytype;

	/*
	 * 如果您在上述或以下列表中添加新属性，
	 * 那么它们也应通过属性API暴露（请参见
	 * 文件顶部的IndexAMProperty，以及utils/adt/amutils.c）。
	 */

	/* 接口函数 */
	ambuild_function ambuild;
	ambuildempty_function ambuildempty;
	aminsert_function aminsert;
	ambulkdelete_function ambulkdelete;
	amvacuumcleanup_function amvacuumcleanup;
	amcanreturn_function amcanreturn;	/* 可以为NULL */
	amcostestimate_function amcostestimate;
	amoptions_function amoptions;
	amproperty_function amproperty; /* 可以为NULL */
	ambuildphasename_function ambuildphasename; /* 可以为NULL */
	amvalidate_function amvalidate;
	amadjustmembers_function amadjustmembers;	/* 可以为NULL */
	ambeginscan_function ambeginscan;
	amrescan_function amrescan;
	amgettuple_function amgettuple; /* 可以为NULL */
	amgetbitmap_function amgetbitmap;	/* 可以为NULL */
	amendscan_function amendscan;
	ammarkpos_function ammarkpos;	/* 可以为NULL */
	amrestrpos_function amrestrpos; /* 可以为NULL */

	/* 支持并行索引扫描的接口函数 */
	amestimateparallelscan_function amestimateparallelscan; /* 可以为NULL */
	aminitparallelscan_function aminitparallelscan; /* 可以为NULL */
	amparallelrescan_function amparallelrescan; /* 可以为NULL */
} IndexAmRoutine;


/* access/index/amapi.c中的函数 */
extern IndexAmRoutine *GetIndexAmRoutine(Oid amhandler);
extern IndexAmRoutine *GetIndexAmRoutineByAmId(Oid amoid, bool noerror);

#endif							/* AMAPI_H */
